//
//  Concurrency.swift
//  cnswift-demo
//
//  Created by wyang on 2022/8/11.
//  Copyright © 2022 YangWei. All rights reserved.
//

import Foundation

class Concurrency: SWDBaseObject {
    
    class func run() {
        let concurrency = Concurrency()
        
        let testAsynchronous = false
        if testAsynchronous {
            // 模拟异步下载控制台输出日志
            
            // 1 定义和调用异步函数
            concurrency.definingAndCallingAsynchronousFunctions()
            
            // 2.1 并行的调用异步方法
            concurrency.callingAsynchronousFunctionsInParallel()
            
            // 4 Actors
            concurrency.actors()
            
            // 5
            concurrency.sendableTypes()
        }
    }
    
    /// 1 定义和调用异步函数
    func definingAndCallingAsynchronousFunctions() {
        let title = "1 定义和调用异步函数";
        printStartLine(title)
        
        Task {
            //  listPhotos(inGallery:) 需要发起网路请求 需要花费较长的时间完成
            print("开始获取 photoNames......")
            let photoNames = await listPhotos(inGallery: "Summer Vacation")
            print("photoNames = \(photoNames)")
            let sortedNames = photoNames.sorted()
            let name = sortedNames[0]
            // downloadPhoto(named:) 需要发起网络请求 需要花费较长的时间完成
            print("开始下载 \(name)......")
            let photo = await downloadPhoto(named: name)
            print("下载结束 photo = \(String(describing: photo))......")
            if photo != nil {
                show(photo ?? Data())
            }
        }
        
        printEndLine(title)
    }
    
    /// 2.1 并行的调用异步方法
    func callingAsynchronousFunctionsInParallel() {
        let title = "2.1 并行的调用异步方法";
        printStartLine(title)
        
        let photoNames = ["IMG001", "IMG99", "IMG0404"];
        
        Task {
            
            let concurrencyDownload = true
            
            if concurrencyDownload {
                // 并发下载多个
                async let firstPhoto = downloadPhoto(named: photoNames[0])
                async let secondPhoto = downloadPhoto(named: photoNames[1])
                async let thirdPhoto = downloadPhoto(named: photoNames[2])
                let photos = await [firstPhoto ?? Data(), secondPhoto ?? Data(), thirdPhoto ?? Data()]
                showDatas(photos)
            } else {
                // 从图库中拉取前三张图片，可以像下面这样，等三次调用完后再执行 downloadPhoto(named:) 函数
                let firstPhoto = await downloadPhoto(named: photoNames[0])
                let secondPhoto = await downloadPhoto(named: photoNames[1])
                let thirdPhoto = await downloadPhoto(named: photoNames[2])
                let photos = [firstPhoto ?? Data(), secondPhoto ?? Data(), thirdPhoto ?? Data()]
                showDatas(photos)
            }
        }
        
        printEndLine(title)
    }
    
    /// 4 Actors
    func actors() {
        let title = "4 Actors";
        printStartLine(title)
        
        let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
        
        Task {
            // 访问 logger.max 是一个可能的悬点
            print(await logger.max)
            // 输出 "25"
            
            // 不添加 await 关键字的情况下访问 logger.max 会失败
//            print(logger.max)  // 报错
            
            printEndLine(title)
        }
    }
    
    /// 5 可发送类型
    func sendableTypes() {
        let title = "5 可发送类型";
        printStartLine(title)
        
        let logger = TemperatureLogger(label: "Tea kettle", measurement: 85)
        let reading = TemperatureReading(measurement: 45)
        
        Task {
            await logger.addReading(from: reading)
            print(await logger.measurements)
            
            printEndLine(title)
        }
    }
}

/// 1 定义和调用异步函数
/// 从图库中拉取图片名称的方法
func listPhotos(inGallery name: String) async -> [String] {
    /*
    let result = // 省略一些异步网络请求代码
    return result
     */
    
    // 使用 sleep() 方法模拟网络请求
    do {
        print("开始网络请求")
        try await Task.sleep(nanoseconds: 2 * 1_000_000_000)  // 两秒
        print("网络请求结束")
        return ["IMG001", "IMG99", "IMG0404"]
    } catch {
        print("网络异常")
        print("未知错误：\(error)")
        return []
    }
}

/// 1 定义和调用异步函数
func downloadPhoto(named: String) async -> Data? {
    // 使用 sleep() 方法模拟网络请求
    do {
        print("下载中:\(named)......")
        try await Task.sleep(nanoseconds: 3 * 1_000_000_000)  // 两秒
        print("下载结束:\(named)✅")
        return Data()
    } catch {
        print("下载异常:\(named)❌")
        print("未知错误：\(error)")
        return nil
    }
}

/// 1 定义和调用异步函数
func show(_ data:Data) {
    print("data = \(data)")
}

/// 2.1 并行的调用异步方法
func showDatas(_ datas:[Data]) {
    print("datas = \(datas)")
}

/// 4 Actors
/// 一个记录温度的 actor
actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    // 限制 max 变量，所以只能在 actor 内部修改最大值
    private(set) var max: Int
    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}

/// 4 Actors
/// 更新 TemperatureLogger 中的温度
extension TemperatureLogger {
    func update(with measurement: Int) {
        measurements.append(measurement)
        if measurement > max {
            max = measurement
        }
    }
}

/// 5 可发送类型
struct TemperatureReading: Sendable {
    var measurement: Int
}

/// 5 可发送类型
extension TemperatureLogger {
    func addReading(from reading: TemperatureReading) {
        measurements.append(reading.measurement)
    }
}

